package randomwalk.socialgraph;

import java.sql.SQLException;

import odnoklassniki.OdnoklassnikiUserTrackSubmatrix;

import randomwalk.PreferencesVector;
import randomwalk.QuizSetVerification;
import randomwalk.Submatrix;
import simpledataset.SimpleMatrix;
import lastfm.LastfmUserTrackSubmatrix;
import musictrackrecommendation.Dataset;
import musictrackrecommendation.ExperimentParameters;
import musictrackrecommendation.Utils;

/**
 * Social graph: (0 | UTr ) (TrU | 0 )
 */
public class UsersTracks implements SocialGraph{
	private static final int STEP_FOR_CONVERGENCE = 3;
	
	/**
	 * @uml.property  name="userTrack"
	 * @uml.associationEnd  
	 */
	private Submatrix userTrack;

	public UsersTracks() throws ClassNotFoundException, SQLException {
		ExperimentParameters parameters = ExperimentParameters.getInstance();
		int datasetType = parameters.getDatasetType();
		switch (datasetType) {
		case ExperimentParameters.LASTFM_SMALL_DATASET:
			userTrack = new LastfmUserTrackSubmatrix();
			normalizeSubmatrices();
			break;
		case ExperimentParameters.SIMPLE_DATASET:
			userTrack = SimpleMatrix.getUserTrack();
			normalizeSubmatrices();
			break;
		case ExperimentParameters.ODNOKLASSNIKI_DATASET:
			userTrack = new OdnoklassnikiUserTrackSubmatrix();
			normalizeSubmatrices();
			break;
		default:
			userTrack = SimpleMatrix.getUserTrack();
			normalizeSubmatrices();
		}
	}

	@Override
	public void normalizeSubmatrices() {
		ExperimentParameters parameters = ExperimentParameters.getInstance();
		int methodCode = parameters.getNormalizationMethod();

		switch (methodCode) {
		case ExperimentParameters.WHOLE_COLUMN_NORMALIZATION:
			normalizeByColumns();
			break;
		case ExperimentParameters.SEPARATE_COLUMN_NORMALIZATION:
			normalizeByColumns();
			break;
		default:
			normalizeByColumns();
			break;
		}
	}
	
	@Override
	public void normalizeByColumns(double uUNorm, double uTrNorm, double trUNorm, double trTrNorm) {
		normalizeByColumns();
	}


	public void normalizeByRows(Submatrix userTrack) {
		ExperimentParameters parameters = ExperimentParameters.getInstance();
		Dataset datasetProperties = parameters.getDatasetProperties();

		int usersCount = datasetProperties.getUsersMaximalId();
		int tracksCount = datasetProperties.getTracksMaximalId();

		double[] usersRowNorms = new double[usersCount + 1];
		double[] tracksRowNorms = new double[tracksCount + 1];

		for (int i = 0; i < userTrack.getRowIds().length; i++) {
			// count UserTrack elements
			tracksRowNorms[userTrack.getColumnIds()[i]] += userTrack
					.getTransponedSubmatrixValues()[i];
			// count TrackUser elements
			usersRowNorms[userTrack.getRowIds()[i]] += userTrack
					.getValues()[i];
		}

		// normalize UserTrack elements
		for (int i = 0; i < userTrack.getValues().length; i++) {
			userTrack.getValues()[i] /= usersRowNorms[userTrack
					.getRowIds()[i]];
			userTrack.getTransponedSubmatrixValues()[i] /= tracksRowNorms[userTrack
					.getColumnIds()[i]];
		}
	}

	@Override
	public void normalizeByColumns() {
		ExperimentParameters parameters = ExperimentParameters.getInstance();
		Dataset datasetProperties = parameters.getDatasetProperties();

		int usersCount = datasetProperties.getUsersMaximalId();
		int tracksCount = datasetProperties.getTracksMaximalId();

		double[] usersColumnNorms = new double[usersCount + 1];
		double[] tracksColumnNorms = new double[tracksCount + 1];

		for (int i = 0; i < userTrack.getRowIds().length; i++) {
			// count UserTrack elements
			tracksColumnNorms[userTrack.getColumnIds()[i]] += userTrack
					.getValues()[i];
			// count TrackUser elements
			usersColumnNorms[userTrack.getRowIds()[i]] += userTrack
					.getTransponedSubmatrixValues()[i];
		}

		// normalize UserTrack elements
		for (int i = 0; i < userTrack.getValues().length; i++) {
			userTrack.getValues()[i] /= tracksColumnNorms[userTrack
					.getColumnIds()[i]];
			userTrack.getTransponedSubmatrixValues()[i] /= usersColumnNorms[userTrack
					.getRowIds()[i]];
		}
	}
	
	@Override
	public double[] randomWalkWithRestarts(PreferencesVector preferencesVector,
			double restartProbability, QuizSetVerification verification, int userId) {

		// restart points of RWR algorithm
		PreferencesVector restartVector = new PreferencesVector(
				preferencesVector.getUsers().clone(), 
				preferencesVector.getTracks().clone(),
				preferencesVector.getTags().clone());
		Utils.multiply(restartVector.getUsers(), restartProbability);
		Utils.multiply(restartVector.getTracks(), restartProbability);
		
		int step = 0;
		
		while (step < STEP_FOR_CONVERGENCE) {
			step++;

			double[] userVector = new double[restartVector.getUsers().length];
			double[] trackVector = new double[restartVector.getTracks().length];

			userTrack.randomWalkTransition(preferencesVector.getTracks(),
					userVector, userId);
			userTrack.randomWalkTransitionTransposition(
					preferencesVector.getUsers(), trackVector);

			// p = (1-a)*S*p + a*q
			Utils.multiply(userVector, 1.0 - restartProbability);
			preferencesVector.setUsers(Utils.sum(restartVector.getUsers(), 
					userVector));

			Utils.multiply(trackVector, 1.0 - restartProbability);
			preferencesVector.setTracks(Utils.sum(restartVector.getTracks(),
					trackVector));
			
			Utils.printStepResults(preferencesVector, verification, userId, step);
		}

		return preferencesVector.getTracks();
	}

}
